package naga.x.proxy;

import java.net.InetSocketAddress;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import naga.x.App;
import naga.x.common.Updatable;
import naga.x.net.proxy.ProxyPacket;
import naga.x.net.proxy.codec.ProxyPacketDecoder;
import naga.x.net.proxy.codec.ProxyPacketEncoder;
import naga.x.service.Service;
import naga.x.service.ServiceException;

/**
 * 代理服与游戏服的连接器
 * @author yang.li
 *
 */
public class GameServerConnector extends IoHandlerAdapter implements Service, Runnable, Updatable {

	protected static final Logger LOG = LoggerFactory.getLogger(GameServerConnector.class);
	
	protected boolean connected;
	protected IoSession session;
	protected ProxyPomeloClientSessionService proxy;
	
	private boolean running;
	private long lastCheckConnectionTime;
	// 等待被转发给客户端的数据包
	private Queue<ProxyPacket> packets = new ConcurrentLinkedQueue<ProxyPacket>();
	
	@Override
	public void startup() throws ServiceException {
		running = true;
		new Thread(this).start();
		App.getApp().getUpdater().add(this);
	}
	
	@Override
	public void shutdown() throws ServiceException {
		running = false;
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	public void sessionCreated(IoSession session) throws Exception {
		this.session = session;
		LOG.info("[GAMESERVER CONNECTED]" + session);
    }

    public void sessionClosed(IoSession session) throws Exception {
    	this.connected = false;
    	this.session = null;
    	LOG.info("[GAMESERVER DISCONNECTED]" + session);
    }

    public void messageReceived(IoSession session, Object message) throws Exception {
    	if (message instanceof ProxyPacket) {
    		packets.offer((ProxyPacket) message);
    	}
    }

	@Override
	public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
		System.out.println("exception:" + session);
		cause.printStackTrace();
	}
	
	private void connect() {
		SocketConnector conn = new NioSocketConnector();
		conn.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ProxyPacketEncoder(), new ProxyPacketDecoder()));
		conn.setHandler(this);
		Config config = App.getApp().getConfig();
		String host = config.gameServerHost;
		int port = config.gameServerPort;
//		String host = App.getApp().getConfig().getString("gameserver.host");
//		int port = App.getApp().getConfig().getIntValue("gameserver.port");
		ConnectFuture cf = conn.connect(new InetSocketAddress(host, port));
		cf.awaitUninterruptibly();
		connected = cf.isConnected();
		Throwable exception = cf.getException();
		if (exception != null) {
			LOG.error("[GAMESERVER CONNECT ERROR]", exception);
		}
	}

	@Override
	public void run() {
		while (running) {
			if (!connected && System.currentTimeMillis() - lastCheckConnectionTime >= 1000) {
				connect();
				lastCheckConnectionTime = System.currentTimeMillis();
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
		}
	}
	
	public boolean isConnected() {
		return connected && session != null && session.isConnected();
	}
	
	protected ProxyPomeloClientSessionService getProxy() {
		if (proxy == null) {
			proxy = App.getApp().getServiceManager().get(ProxyPomeloClientSessionService.class);
		}
		return proxy;
	}
	
	/**
	 * 转发客户端的数据包给游戏服
	 * @param packet
	 */
	public void send(ProxyPacket packet) {
		if (isConnected()) {
			session.write(packet);
		}
	}

	@Override
	public boolean update() {
		ProxyPacket packet;
		int c = 0;
		ProxyPomeloClientSessionService proxy = getProxy();
		while ((packet = packets.poll()) != null) {
			proxy.dispatch(packet);
			if (++c == 1024) {
				break;
			}
		}
		return true;
	}

}
